home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue43 / comcorn / AxDocs.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-02-12  |  17.1 KB  |  558 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {       ActiveX Document Support Unit                   }
  4. {       Copyright (c) 1999, Steve Teixeira              }
  5. {                                                       }
  6. {*******************************************************}
  7.  
  8. unit AxDocs;
  9.  
  10. interface
  11.  
  12. uses
  13.   Windows, ComObj, ActiveX, AxCtrls, Controls, Classes, Menus, Messages;
  14.  
  15. type
  16.   TActiveXDocumentFactory = class;
  17.  
  18.   TActiveXDocument = class(TActiveXControl, IOleDocument, IOleDocumentView,
  19.     IOleInPlaceActiveObject, IOleInPlaceObject)
  20.   private
  21.     FFactory: TActiveXDocumentFactory;
  22.     FMenu: TMainMenu;
  23.     FOleMenu: HMENU;
  24.     FSharedMenu: HMENU;
  25.     function GetAncestorValueByField(FieldNum: Cardinal): Cardinal;
  26.     procedure SetAncestorValueByField(FieldNum, Value: Cardinal);
  27.     function GetOleInPlaceSite: IOleInPlaceSite;
  28.     procedure SetOleInPlaceSite(const Value: IOleInPlaceSite);
  29.     procedure InPlaceMenuCreate;
  30.     procedure InPlaceMenuDestroy;
  31.     procedure MergeMenus(SharedMenu, SourceMenu: HMENU;
  32.       MenuWidths: PInteger; WidthIndex: Integer);
  33.     procedure UnmergeMenus(SharedMenu, SourceMenu: HMENU);
  34.   protected
  35.     { IOleDocument methods }
  36.     function CreateView(Site: IOleInPlaceSite; Stream: IStream; rsrvd: DWORD;
  37.       out View: IOleDocumentView):HResult; stdcall;
  38.     function GetDocMiscStatus(var Status: DWORD):HResult; stdcall;
  39.     function EnumViews(out Enum: IEnumOleDocumentViews;
  40.       out View: IOleDocumentView):HResult; stdcall;
  41.     { IOleDocumentView methods }
  42.     function SetInPlaceSite(Site: IOleInPlaceSite): HResult; stdcall;
  43.     function GetInPlaceSite(out Site: IOleInPlaceSite): HResult; stdcall;
  44.     function GetDocument(out P: IUnknown): HResult; stdcall;
  45.     function SetRect(const View: TRECT): HResult; stdcall;
  46.     function GetRect(var View: TRECT): HResult; stdcall;
  47.     function SetRectComplex(const View, HScroll, VScroll, SizeBox): HResult; stdcall;
  48.     function Show(fShow: BOOL): HResult; stdcall;
  49.     function UIActivate(fUIActivate: BOOL): HResult; stdcall;
  50.     function Open: HResult; stdcall;
  51.     function CloseView(dwReserved: DWORD): HResult; stdcall;
  52.     function SaveViewState(pstm: IStream): HResult; stdcall;
  53.     function ApplyViewState(pstm: IStream): HResult; stdcall;
  54.     function Clone(NewSite: IOleInPlaceSite; out NewView: IOleDocumentView):HResult; stdcall;
  55.     { IOleInPlaceActiveObject }
  56.     function OnDocWindowActivate(fActivate: BOOL): HResult; stdcall;
  57.     { IOleInPlaceObject }
  58.     function InPlaceDeactivate: HResult; stdcall;
  59.     { Overrides }
  60.     procedure GetDocUIInfo(var Menu: TMainMenu);
  61.     function InPlaceActivate(ActivateUI: Boolean): HResult; override;
  62.     procedure WndProc(var Message: TMessage); override;
  63.   public
  64.     procedure Initialize; override;
  65.     function ObjQueryInterface(const IID: TGUID; out Obj): HResult; override;
  66.     property Menu: TMainMenu read FMenu write FMenu;
  67.     property OleInPlaceSite: IOleInPlaceSite read GetOleInPlaceSite write SetOleInPlaceSite;
  68.   end;
  69.  
  70.   TActiveXDocClass = class of TActiveXDocument;
  71.  
  72.   TActiveXDocumentFactory = class(TActiveXControlFactory)
  73.   private
  74.     FDocMiscStatus: DWORD;
  75.     FHandler: string;
  76.   public
  77.     property DocMiscStatus: DWORD read FDocMiscStatus;
  78.     constructor Create(ComServer: TComServerObject;
  79.       ActiveXDocClass: TActiveXDocClass; WinControlClass: TWinControlClass;
  80.       const ClassID: TGUID; ToolboxBitmapID, MiscStatus: Integer;
  81.       ThreadingModel: TThreadingModel; const Handler: string;
  82.       DocMiscStatus: DWORD);
  83.     procedure UpdateRegistry(Register: Boolean); override;
  84.   end;
  85.  
  86. implementation
  87.  
  88. uses ComServ, SysUtils, Forms;
  89.  
  90. { TActiveXDocument }
  91.  
  92. function TActiveXDocument.ObjQueryInterface(const IID: TGUID; out Obj): HResult;
  93. begin
  94.   // Must stub out IOleLink, or container will assume this is a linked object
  95.   // rather than an embedded object.
  96.   if IsEqualGuid(IID, IOleLink) then Result := E_NOINTERFACE
  97.   else Result := inherited ObjQueryInterface(IID, Obj);
  98. end;
  99.  
  100. function TActiveXDocument.GetOleInPlaceSite: IOleInPlaceSite;
  101. begin
  102.   // Work around fact that FOleInPlaceSite is private in TActiveXControl
  103.   // Note: this work around only guaranteed to work in Delphi 4
  104.   Result := IOleInPlaceSite(GetAncestorValueByField(9));
  105. end;
  106.  
  107. procedure TActiveXDocument.SetOleInPlaceSite(const Value: IOleInPlaceSite);
  108. begin
  109.   // Work around fact that FOleInPlaceSite is private in TActiveXControl
  110.   // Note: this work around only guaranteed to work in Delphi 4
  111.   SetAncestorValueByField(9, Cardinal(Value));
  112. end;
  113.  
  114. function TActiveXDocument.GetAncestorValueByField(FieldNum: Cardinal): Cardinal;
  115. var
  116.   ParentInstanceSize, Ofs: Cardinal;
  117. begin
  118.   // Nasty hack: this method returns the value of a particular field in the
  119.   // ancestor class, with the assumption that the given field and all prior
  120.   // fields are 4 bytes in size.
  121.   ParentInstanceSize := ClassParent.ClassParent.InstanceSize;
  122.   Ofs := ParentInstanceSize + ((FieldNum - 1) * 4);
  123.   asm
  124.     mov eax, Self
  125.     add eax, Ofs
  126.     mov eax, dword ptr [eax]
  127.     mov @Result, eax
  128.   end;
  129. end;
  130.  
  131. procedure TActiveXDocument.SetAncestorValueByField(FieldNum, Value: Cardinal);
  132. var
  133.   ParentInstanceSize, Ofs: Cardinal;
  134. begin
  135.   // Nasty hack: this method sets the value of a particular field in the
  136.   // ancestor class, with the assumption that the given field and all prior
  137.   // fields are 4 bytes in size.
  138.   ParentInstanceSize := ClassParent.ClassParent.InstanceSize;
  139.   Ofs := ParentInstanceSize + ((FieldNum - 1) * 4);
  140.   asm
  141.     mov eax, Self
  142.     add eax, Ofs
  143.     mov ecx, Value
  144.     mov dword ptr [eax], ecx
  145.   end;
  146. end;
  147.  
  148. procedure TActiveXDocument.Initialize;
  149. begin
  150.   inherited Initialize;
  151.   FFactory := Factory as TActiveXDocumentFactory;
  152. end;
  153.  
  154. procedure TActiveXDocument.GetDocUIInfo(var Menu: TMainMenu);
  155. begin
  156.   Menu := nil;
  157. end;
  158.  
  159. function TActiveXDocument.InPlaceActivate(ActivateUI: Boolean): HResult;
  160. begin
  161.   Result := inherited InPlaceActivate(ActivateUI);
  162.   InPlaceMenuCreate;
  163. end;
  164.  
  165. procedure TActiveXDocument.WndProc(var Message: TMessage);
  166. begin
  167.   inherited WndProc(Message);
  168.   if Message.Msg = WM_LBUTTONDBLCLK then InPlaceActivate(True);
  169. end;
  170.  
  171. procedure TActiveXDocument.InPlaceMenuCreate;
  172. var
  173.   IPFrame: IOleInPlaceFrame;
  174.   IPSite: IOleInPlaceSite;
  175.   IPUIWindow: IOleInPlaceUIWindow;
  176.   omgw: TOleMenuGroupWidths;
  177.   FrameInfo: TOleInPlaceFrameInfo;
  178.   PosRect, ClipRect: TRect;
  179. begin
  180.   OleCheck(ClientSite.QueryInterface(IOleInPlaceSite, IPSite));
  181.   FrameInfo.cb := sizeof(FrameInfo);
  182.   IPSite.GetWindowContext(IPFrame, IPUIWindow, PosRect, ClipRect, FrameInfo);
  183.   FillChar(omgw, SizeOf(omgw), 0);
  184.   omgw[1] := 1;
  185.   // Create a blank menu and ask the container to add it's menus into the
  186.   // TOleMenuGroupWidths record
  187.   FSharedMenu := CreateMenu;
  188.   try
  189.     OleCheck(IPFrame.InsertMenus(FSharedMenu, omgw));
  190.     if FMenu = nil then Exit;
  191.     MergeMenus(FSharedMenu, FMenu.Handle, @omgw.width, 1);
  192.     // Send the menu to the client
  193.     FOleMenu := OleCreateMenuDescriptor(FSharedMenu, omgw);
  194.     IPFrame.SetMenu(FSharedMenu, FOleMenu, Control.Handle);
  195.   except
  196.     DestroyMenu(FSharedMenu);
  197.     FSharedMenu := 0;
  198.     raise;
  199.   end;
  200. end;
  201.  
  202. procedure TActiveXDocument.InPlaceMenuDestroy;
  203. var
  204.   IPFrame: IOleInPlaceFrame;
  205.   IPSite: IOleInPlaceSite;
  206.   IPUIWindow: IOleInPlaceUIWindow;
  207.   FrameInfo: TOleInPlaceFrameInfo;
  208.   PosRect, ClipRect: TRect;
  209. begin
  210.   // Get the clients IOleInPlaceFrame so we can ask it to remove it's menu
  211.   OleCheck(ClientSite.QueryInterface(IOleInPlaceSite, IPSite));
  212.   FrameInfo.cb := sizeof(FrameInfo);
  213.   IPSite.GetWindowContext(IPFrame, IPUIWindow, PosRect, ClipRect, FrameInfo);
  214.   if IPFrame <> nil then IPFrame.SetMenu(0, 0, 0);
  215.   OleDestroyMenuDescriptor(FOleMenu);
  216.   FOleMenu := 0;
  217.   UnmergeMenus(FSharedMenu, FMenu.Handle);
  218. end;
  219.  
  220. type
  221.   PIntArray = ^TIntArray;
  222.   TIntArray = array[0..0] of Integer;
  223.  
  224. procedure TActiveXDocument.MergeMenus(SharedMenu, SourceMenu: HMENU;
  225.   MenuWidths: PInteger; WidthIndex: Integer);
  226. var
  227.   MenuItems, GroupWidth, Position, I, Len: Integer;
  228.   MenuState: UINT;
  229.   PopupMenu: HMENU;
  230.   ItemText: array[0..255] of char;
  231. begin
  232.   // Copy the popups from the pMenuSource
  233.   MenuItems := GetMenuItemCount(SourceMenu);
  234.   GroupWidth := 0;
  235.   Position := 0;
  236.   // Insert at appropriate spot depending on WidthIndex
  237.   if (WidthIndex < 0) or (WidthIndex > 1) then Exit;
  238.   if WidthIndex = 1 then Position := MenuWidths^;
  239.   for I := 0 to MenuItems - 1 do
  240.   begin
  241.     // Get the HMENU of the popup
  242.     PopupMenu := GetSubMenu(SourceMenu, I);
  243.     // Separators move us to next group
  244.     MenuState := GetMenuState(SourceMenu, I, MF_BYPOSITION);
  245.     if (PopupMenu = NULL) and ((MenuState and MF_SEPARATOR) <> 0) then
  246.     begin
  247.       if WidthIndex > 5 then Exit;     // Servers should not touch past 5
  248.       PIntArray(MenuWidths)^[WidthIndex] := GroupWidth;
  249.       GroupWidth := 0;
  250.       if WidthIndex < 5 then
  251.         Inc(Position, PIntArray(MenuWidths)^[WidthIndex + 1]);
  252.       Inc(WidthIndex, 2);
  253.     end
  254.     else begin
  255.       // Get the menu item text
  256.       Len := GetMenuString(SourceMenu, I, ItemText, SizeOf(ItemText), MF_BYPOSITION);
  257.       // Popups are handled differently than normal menu items
  258.       if PopupMenu <> 0 then
  259.       begin
  260.         if GetMenuItemCount(PopupMenu) <> 0 then
  261.         begin
  262.           // Strip the HIBYTE because it contains a count of items
  263.           MenuState := LoByte(MenuState) or MF_POPUP;   // Must be popup
  264.           // Non-empty popup -- add it to the shared menu bar
  265.           InsertMenu(SharedMenu, Position, MenuState or MF_BYPOSITION, PopupMenu,
  266.             ItemText);
  267.           Inc(Position);
  268.           Inc(GroupWidth);
  269.         end;
  270.       end
  271.       else if Len > 0 then
  272.       begin
  273.         // only non-empty items are added
  274.         if ItemText <> '' then
  275.         begin
  276.           // here the state does not contain a count in the HIBYTE
  277.           InsertMenu(SharedMenu, Position, MenuState or MF_BYPOSITION,
  278.             GetMenuItemID(SourceMenu, I), ItemText);
  279.           Inc(Position);
  280.           Inc(GroupWidth);
  281.         end;
  282.       end;
  283.     end;
  284.   end;
  285. end;
  286.  
  287. procedure TActiveXDocument.UnmergeMenus(SharedMenu, SourceMenu: HMENU);
  288. var
  289.   TheseItems, MenuItems, I, J: Integer;
  290.   PopupMenu: HMENU;
  291. begin
  292.   MenuItems := GetMenuItemCount(SharedMenu);
  293.   TheseItems := GetMenuItemCount(SourceMenu);
  294.   for I := MenuItems - 1 downto 0 do
  295.   begin
  296.     // Check the popup menus
  297.     PopupMenu := GetSubMenu(SharedMenu, I);
  298.     if PopupMenu <> 0 then
  299.     begin
  300.       // If it is one of ours, remove it from the SharedMenu
  301.       for J := 0 to TheseItems - 1 do
  302.       begin
  303.         if GetSubMenu(SourceMenu, J) = PopupMenu then
  304.         begin
  305.           // Remove the menu from SharedMenu
  306.           RemoveMenu(SharedMenu, I, MF_BYPOSITION);
  307.           Break;
  308.         end;
  309.       end;
  310.     end;
  311.   end;
  312. end;
  313.  
  314. { TActiveXDocument.IOleDocument }
  315.  
  316. function TActiveXDocument.CreateView(Site: IOleInPlaceSite;
  317.   Stream: IStream; rsrvd: DWORD; out View: IOleDocumentView): HResult;
  318. var
  319.   OleDocView: IOleDocumentView;
  320. begin
  321.   Result := S_OK;
  322.   try
  323.     if View = nil then
  324.     begin
  325.       Result := E_POINTER;
  326.       Exit;
  327.     end;
  328.     OleDocView := Self as IOleDocumentView;
  329.     if (OleInPlaceSite = nil) or (OleDocView = nil) then
  330.     begin
  331.       Result := E_FAIL;
  332.       Exit;
  333.     end;
  334.     // Use site provided
  335.     if Site <> nil then OleDocView.SetInPlaceSite(Site);
  336.     // Use stream provided for initialization
  337.     if Stream <> nil then OleDocView.ApplyViewState(Stream);
  338.     // Return the view
  339.     View := OleDocView;
  340.   except
  341.     Result := E_FAIL;
  342.   end;
  343. end;
  344.  
  345. function TActiveXDocument.EnumViews(out Enum: IEnumOleDocumentViews;
  346.   out View: IOleDocumentView): HResult;
  347. begin
  348.   Result := S_OK;
  349.   try
  350.     // We only support one view
  351.     View := Self as IOleDocumentView;
  352.   except
  353.     Result := E_FAIL;
  354.   end;
  355. end;
  356.  
  357. function TActiveXDocument.GetDocMiscStatus(var Status: DWORD): HResult;
  358. begin
  359.   Status := (Factory as TActiveXDocumentFactory).DocMiscStatus;
  360.   Result := S_OK;
  361. end;
  362.  
  363. { TActiveXDocument.IOleDocument }
  364.  
  365. function TActiveXDocument.ApplyViewState(pstm: IStream): HResult;
  366. begin
  367.   Result := E_NOTIMPL;
  368. end;
  369.  
  370. function TActiveXDocument.Clone(NewSite: IOleInPlaceSite;
  371.   out NewView: IOleDocumentView): HResult;
  372. begin
  373.   Result := E_NOTIMPL;
  374. end;
  375.  
  376. function TActiveXDocument.CloseView(dwReserved: DWORD): HResult;
  377. begin
  378.   Result := S_OK;
  379.   try
  380.     Show(False);
  381.     SetInPlaceSite(nil);
  382.   except
  383.     Result := E_UNEXPECTED;
  384.   end;
  385. end;
  386.  
  387. function TActiveXDocument.GetDocument(out P: IUnknown): HResult;
  388. begin
  389.   Result := S_OK;
  390.   try
  391.     P := Self as IUnknown;
  392.   except
  393.     Result := E_FAIL;
  394.   end;
  395. end;
  396.  
  397. function TActiveXDocument.GetInPlaceSite(out Site: IOleInPlaceSite): HResult;
  398. begin
  399.   Result := S_OK;
  400.   try
  401.     Site := OleInPlaceSite;
  402.   except
  403.     Result := E_FAIL;
  404.   end;
  405. end;
  406.  
  407. function TActiveXDocument.GetRect(var View: TRECT): HResult;
  408. begin
  409.   Result := S_OK;
  410.   try
  411.     View := Control.BoundsRect;
  412.   except
  413.     Result := E_UNEXPECTED;
  414.   end;
  415. end;
  416.  
  417. function TActiveXDocument.Open: HResult;
  418. begin
  419.   Result := E_NOTIMPL;
  420. end;
  421.  
  422. function TActiveXDocument.SaveViewState(pstm: IStream): HResult;
  423. begin
  424.   Result := E_NOTIMPL;
  425. end;
  426.  
  427. function TActiveXDocument.SetInPlaceSite(Site: IOleInPlaceSite): HResult;
  428. begin
  429.   Result := S_OK;
  430.   try
  431.     if OleInPlaceSite <> nil then
  432.       Result := InPlaceDeactivate;
  433.     if Result <> S_OK then Exit;
  434.     if Site <> nil then OleInPlaceSite := Site;
  435.   except
  436.     Result := E_UNEXPECTED;
  437.   end;
  438. end;
  439.  
  440. function TActiveXDocument.SetRect(const View: TRECT): HResult;
  441. begin
  442.   // Implement using TActiveXControl's IOleInPlaceObject.SetObjectRects impl
  443.   Result := SetObjectRects(View, View);
  444. end;
  445.  
  446. function TActiveXDocument.SetRectComplex(const View; const HScroll;
  447.   const VScroll; const SizeBox): HResult;
  448. begin
  449.   Result := E_NOTIMPL;
  450. end;
  451.  
  452. function TActiveXDocument.Show(fShow: BOOL): HResult;
  453. begin
  454.   try
  455.     if fShow then
  456.       Result := InPlaceActivate(False)
  457.     else begin
  458.       Result := UIActivate(False);
  459.       Control.Visible := False;
  460.     end;
  461.   except
  462.     Result := E_UNEXPECTED;
  463.   end;
  464. end;
  465.  
  466. function TActiveXDocument.UIActivate(fUIActivate: BOOL): HResult;
  467. begin
  468.   Result := S_OK;
  469.   try
  470.     if FUIActivate then
  471.     begin
  472.       if OleInPlaceSite <> nil then InPlaceActivate(True)
  473.       else Result := E_UNEXPECTED;
  474.     end
  475.     else begin
  476.       UIDeactivate;
  477.       InPlaceMenuDestroy;
  478.     end;
  479.   except
  480.     Result := E_UNEXPECTED;
  481.   end;
  482. end;
  483.  
  484. { TActiveXDocument.IOleInPlaceActiveObject }
  485.  
  486. function TActiveXDocument.OnDocWindowActivate(fActivate: BOOL): HResult;
  487. begin
  488.   Result := inherited OnDocWindowActivate(fActivate);
  489.   if fActivate then InPlaceMenuCreate
  490.   else InPlaceMenuDestroy;
  491. end;
  492.  
  493. { TActiveXDocument.IOleInPlaceObject }
  494.  
  495. function TActiveXDocument.InPlaceDeactivate: HResult;
  496. var
  497.   ParentWnd: HWND;
  498. begin
  499.   // This is a work-around for the fact that TActiveXControl implementation of
  500.   // this method makes the control go away to ParkingWindow la-la land.  It
  501.   // needs to stay put within the document.
  502.   ParentWnd := Control.ParentWindow;
  503.   Result := inherited InplaceDeactivate;
  504.   Control.ParentWindow := ParentWnd;
  505.   Control.Visible := True;
  506. end;
  507.  
  508. { TActiveXDocumentFactory }
  509.  
  510. constructor TActiveXDocumentFactory.Create(ComServer: TComServerObject;
  511.   ActiveXDocClass: TActiveXDocClass; WinControlClass: TWinControlClass;
  512.   const ClassID: TGUID; ToolboxBitmapID, MiscStatus: Integer;
  513.   ThreadingModel: TThreadingModel; const Handler: string;
  514.   DocMiscStatus: DWORD);
  515. begin
  516.   FDocMiscStatus := DocMiscStatus;
  517.   if Handler <> '' then FHandler := Handler
  518.   else FHandler := 'ole32.dll';
  519.   inherited Create(ComServer, ActiveXDocClass, WinControlClass, ClassId,
  520.     ToolboxBitmapID, '', MiscStatus, ThreadingModel);
  521. end;
  522.  
  523. procedure TActiveXDocumentFactory.UpdateRegistry(Register: Boolean);
  524. var
  525.   ClassKey, ProgKey, MiscFlags: string;
  526. begin
  527.   ClassKey := 'CLSID\' + GUIDToString(ClassID) + '\';
  528.   ProgKey := ProgID + '\';
  529.   if Register then
  530.   begin
  531.     inherited UpdateRegistry(Register);
  532.     MiscFlags := IntToStr(FDocMiscStatus);
  533.     // Add reg keys under CLSID
  534.     CreateRegKey(ClassKey + 'DocObject', '', MiscFlags);
  535.     CreateRegKey(ClassKey + 'Programmable', '', '');
  536.     CreateRegKey(ClassKey + 'Insertable', '', '');
  537.     CreateRegKey(ClassKey + 'InprocHandler32', '', FHandler);
  538.     // Add reg keys under ProgID
  539.     CreateRegKey(ProgKey + 'DocObject', '', MiscFlags);
  540.     CreateRegKey(ProgKey + 'Insertable', '', '');
  541.     // Need to remove "control" key added by inherited method
  542.     DeleteRegKey(ClassKey + 'Control');
  543.   end
  544.   else begin
  545.     DeleteRegKey(ClassKey + 'DefaultExtension');
  546.     DeleteRegKey(ClassKey + 'DefaultIcon');
  547.     DeleteRegKey(ClassKey + 'DocObject');
  548.     DeleteRegKey(ClassKey + 'Programmable');
  549.     DeleteRegKey(ClassKey + 'Insertable');
  550.     DeleteRegKey(ClassKey + 'InprocHandler32');
  551.     DeleteRegKey(ProgKey + 'DocObject');
  552.     DeleteRegKey(ProgKey + 'Insertable');
  553.     inherited UpdateRegistry(Register);
  554.   end;
  555. end;
  556.  
  557. end.
  558.